import os
import uuid
import re
from datetime import datetime

import pandas as pd

from configs.db_connector import ProvincialPlatformConfigDev
from utils.db_config_utils import JdbcConfigUtils
from utils.json_utils import load_json

csv_to_table_mapping = {
    "食材采购统计": "sunshine_report_food_procurement_statistics",
    "食材出入库统计": "sunshine_report_food_storage_statistics",
    "食堂消费统计": "sunshine_report_canteen_consumption_statistics",
    "食堂充值月统计": "sunshine_report_canteen_recharge_monthly_statistics",
    "食堂专户流水": "sunshine_report_canteen_special_account_flow",
    "供应商结算月统计": "sunshine_report_supplier_settlement_monthly_statistics",
    "食堂基本信息": "sunshine_basic_school_canteen_info",
    "学校基本信息": "sunshine_basic_school_basic_info",
    "学校食堂对应关系": "sunshine_basic_school_canteen_mapping",
    "供应商基本信息": "sunshine_basic_supplier_basic_info",
    "食堂专户信息": "sunshine_basic_canteen_special_account_info",
    "食堂月度科目信息": "sunshine_report_subject_monthly_statistics"
}

# 以下表可以支持修改覆盖操作
base_info_csv_to_table_mapping = {

    "食堂基本信息": "sunshine_basic_school_canteen_info",
    "学校基本信息": "sunshine_basic_school_basic_info",
    "学校食堂对应关系": "sunshine_basic_school_canteen_mapping",
    "供应商基本信息": "sunshine_basic_supplier_basic_info",
    "食堂专户信息": "sunshine_basic_canteen_special_account_info",
}
# 统计表数据，不支持覆盖操作
count_csv_to_table_mapping = {
    "食材采购统计": "sunshine_report_food_procurement_statistics",
    "食材出入库统计": "sunshine_report_food_storage_statistics",
    "食堂消费统计": "sunshine_report_canteen_consumption_statistics",
    "食堂充值月统计": "sunshine_report_canteen_recharge_monthly_statistics",
    "食堂专户流水": "sunshine_report_canteen_special_account_flow",
    "供应商结算月统计": "sunshine_report_supplier_settlement_monthly_statistics",
    "食堂月度科目信息": "sunshine_report_subject_monthly_statistics"
}

keys_to_delete = [
              "食堂基本信息",
              "学校基本信息",
              "学校食堂对应关系",
              "供应商基本信息",
              "食堂专户信息",
                      ]
# 用户与地区的映射关系
sftp_user_mapping = {
    "xiaogansftp": "孝感市",
    "gsyichangsftp": "宜昌市",
    "suizhousftp": "随州市",
    "shiyansftp": "十堰市",
    "shennongjiasftp": "神农架林区",
    "jingzhousftp": "荆州市",
    "jingmensftp": "荆门市",
    "huangshisftp": "黄石市",
    "huanggangsftp": "黄冈市",
    "gsxiantaosftp": "仙桃市",
    "gsxianningsftp": "咸宁市",
    "gsshiyansftp": "十堰市",
    "gsshennongjiasftp": "神农架林区",
    "gshuangshisftp": "黄石市",
    "gshuanggangsftp": "黄冈市",
    "gsenshisftp": "恩施土家族苗族自治州",
    "gsjingzhousftp": "荆州市",
    "gsqianjiangsftp": "潜江市",
    "gsxiangyangsftp": "襄阳市",
    "qianjiangsftp": "潜江市",
    "ezhousftp": "鄂州市",
    "enshisftp": "恩施土家族苗族自治州",
    "yichangsftp": "宜昌市",
    "xiaoganprod": "孝感市",
    "xiantaosftp": "仙桃市",
    "xianningsftp": "咸宁市",
    "xiangyangsftp": "襄阳市",
    "wuhansftp": "武汉市",
    "tianmensftp": "天门市",
    "yilingqusftp": "宜昌市"
}
class SftpCleanData:
    @staticmethod
    def is_integer(value):
        """检查值是否可以转换为整数"""
        try:
            int(value)
            return True
        except (ValueError, TypeError):
            return False

    @classmethod
    def clean_data(cls, row, headers, integer_columns):
        """清理行数据，确保整数字段有有效的整数值，空值设为-1"""
        cleaned_row = []
        for idx, value in enumerate(row):
            if headers[idx] in integer_columns:
                # 对于整数列，尝试转换为整数，如果为空或无法转换则设置为-1
                cleaned_value = int(value) if cls.is_integer(value) and value.strip() != '' else -1
                cleaned_row.append(cleaned_value)
            else:
                cleaned_row.append(value if value.strip() != '' else None)  # 非整数列可选处理空字符串
        return cleaned_row

    @classmethod
    def batch_insert_from_folder(cls,folder_path):
        """
        批量插入文件夹中的 CSV 文件到数据库，并将插入失败的信息记录到日志文件中
        """
        # 错误日志文件路径
        error_log_path = os.path.join(folder_path, "insert_error_log.txt")

        # 打开错误日志文件（追加模式）
        with open(error_log_path, "a", encoding="utf-8") as error_log:
            # 遍历文件夹中的所有文件
            for filename in os.listdir(folder_path):
                # 检查文件是否是 CSV 文件
                if filename.endswith(".csv"):
                    # 获取文件名（不带扩展名）
                    file_basename = os.path.splitext(filename)[0]

                    # 根据第一个下划线切割文件名
                    mapping_key = file_basename.split("_")[1]  # 取第一个下划线前的部分

                    # 根据切割后的名称查找对应的表名
                    if mapping_key in csv_to_table_mapping:
                        table_name = csv_to_table_mapping[mapping_key]
                        csv_file_path = os.path.join(folder_path, filename)

                        try:
                            # 读取 CSV 文件
                            df = pd.read_csv(csv_file_path, dtype={
                                'bank_card_num': str,
                                'some_numeric_column': 'int64'
                            })
                            if base_info_csv_to_table_mapping.get(mapping_key,None) is not None:
                                if 'gsxiangyangsftp' in folder_path:
                                    SftpCleanData.delete_data_by_city_districts([mapping_key],'襄阳市',['老河口市'])
                                elif  'yilingqusftp' in folder_path:
                                    SftpCleanData.delete_data_by_city_districts([mapping_key],
                                                                                '宜昌市', ['夷陵区'])
                                else:
                                    cls.batch_delete_from_csv_data_v2(df, mapping_key)
                            # 调用插入方法插入数据库
                            df['del_flag'] = 0
                            JdbcConfigUtils.insert_table_data(ProvincialPlatformConfigDev.DATABASE_NAME, table_name, df,
                                                              ProvincialPlatformConfigDev)
                            print(f"成功插入文件: {filename} 到表: {table_name}")
                        except Exception as e:
                            # 插入失败，记录错误信息到日志文件
                            error_msg = f"\n\n\n=======文件 {filename} 插入失败===============================\n\n\n，错误信息: {e}"
                            print(error_msg)
                            error_log.write(error_msg + "\n")
                    else:
                        # 未找到对应的表映射，记录到日志文件
                        error_msg = f"文件 {filename} 未找到对应的表映射，跳过处理。"
                        print(error_msg)
                        error_log.write(error_msg + "\n")

        print(f"插入完成，错误日志已保存到: {error_log_path}")


    @staticmethod
    def process_csv_files(folder_path):
        """
            递归遍历文件夹及子文件夹中的所有文件，尝试读取 CSV 文件。
            如果文件读取失败或不是 CSV 文件，将错误信息保存到 error_log.txt 中。
            :param folder_path: 读取文件夹路径
        """
        # 清洗后的文件保存路径
        clear_file_path = folder_path+"_cleanfile"
        # 获取账号中文名称
        area_ftp_name = folder_path.split('\\')[-1]
        city_name = sftp_user_mapping.get(area_ftp_name, None)
        # 反转地区字典，方便通过名称查找编码
        area_dict = load_json('../static/hubei_area_data.json')
        # 进行翻转
        area_dict = SftpCleanData.invert_area_dict(area_dict)
        error_log = []  # 用于保存错误信息
        # 获取该地区的市code
        try:
            code = area_dict[city_name]['name']
        except Exception as e:
            print("报错："+clear_file_path)
            error_log.append(e)

        district_name_to_code = {k: v for k, v in area_dict[city_name]['children'].items()}



        # 确保清洗后的文件夹存在
        if not os.path.exists(clear_file_path):
            os.makedirs(clear_file_path)

        # 用于记录已处理的文件，避免重复处理
        processed_files = set()

        # 递归遍历文件夹及子文件夹
        for root, dirs, files in os.walk(folder_path):
            for filename in files:
                file_path = os.path.join(root, filename)

                # 检查文件是否已经处理过
                if file_path in processed_files:
                    continue
                processed_files.add(file_path)

                # 检查文件是否是 CSV 文件
                if filename.endswith(".csv"):
                    try:
                        # 尝试用 UTF-8 编码读取文件
                        df = pd.read_csv(file_path, encoding='utf-8')
                        print(f"成功读取文件: {file_path}")
                    except UnicodeDecodeError:
                        try:
                            # 如果 UTF-8 失败，尝试用 GBK 编码读取文件
                            df = pd.read_csv(file_path, encoding='gbk')
                            print(f"成功读取文件: {file_path} (GBK 编码)")
                        except UnicodeDecodeError:
                            try:
                                # 如果 GBK 也失败，尝试用 ISO-8859-1 编码读取文件
                                df = pd.read_csv(file_path, encoding='iso-8859-1')
                                print(f"成功读取文件: {file_path} (ISO-8859-1 编码)")
                            except UnicodeDecodeError:
                                error_msg = f"{file_path}: 编码异常，非 utf-8、gbk、iso-8859-1 编码之一，请确保 CSV 编码格式！"
                                error_log.append(error_msg)
                                continue  # 跳过当前文件，继续处理下一个文件
                            except Exception as e:
                                error_msg = f"{file_path}: 读取失败，错误信息: {e}"
                                error_log.append(error_msg)
                                continue
                        except Exception as e:
                            error_msg = f"{file_path}: 读取失败，错误信息: {e}"
                            error_log.append(error_msg)
                            continue
                    except Exception as e:
                        error_msg = f"{file_path}: 读取失败，错误信息: {e}"
                        error_log.append(error_msg)
                        continue

                    # 添加省编码（固定为420000）
                    df["province_code"] = "420000"
                    try:
                        for index, row in df.iterrows():
                            if '湖北省' not in row['province']:
                                error_msg = f"{file_path}: 读取失败，非湖北省信息: {row['province']}"
                                print(f"{file_path}: 读取失败，非湖北省信息: {row['province']}")
                                error_log.append(error_msg)
                                continue
                    except Exception as e:
                        error_log.append(f"{filename}====>{e}")

                    # 市直转化
                    # 检查并替换'district'列中包含'市直'的值
                    # 使用fillna确保缺失值被空字符串替代，避免在apply中出现NoneType错误
                    df['district'] = df['district'].fillna('')
                    # 使用apply方法并结合lambda函数来实现你的需求
                    df['district'] = df['district'].apply(
                        lambda x: f'{city_name}直' if '市直' in x or '市辖区' in x or x.strip() == city_name else x
                    )
                    # 生成 UUID 并添加到 DataFrame
                    df['id'] = [str(uuid.uuid4()) for _ in range(len(df))]

                    # 生成 UUID 并添加到 DataFrame
                    df['id'] = [str(uuid.uuid4()) for _ in range(len(df))]
                    # 市名称根据登录账号进行统一转换
                    # 检查是否包含'city'列
                    if 'city' not in df.columns:
                        print("CSV文件中缺少必要的'city'列")
                        return


                    if city_name is None:
                        print(f"未找到与{area_ftp_name}相关的城市名称映射")
                        return
                    df['city'] = city_name
                    # 根据市名称转换为市编码
                    df["city_code"] = code

                    # 根据区名称转换为区编码
                    if "district" in df.columns:
                        # 遍历 DataFrame 中的每一条记录
                        for index, row in df.iterrows():
                            district = SftpCleanData.clean_text(row['district'])
                            # 尝试从 district_name_to_code 字典中获取对应的 district_code
                            district_code = district_name_to_code.get(district, '')  # 如果不存在，则返回空字符串
                            df.at[index, 'district_code'] = district_code  # 更新当前行的 'district_code' 值

                    else:
                        print(f"Warning: 'district' column not found in {filename}. Skipping district_code conversion.")
                    current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                    # 新增一列create_time设置为当前时间
                    df['create_time'] = current_time

                    # 生成时间戳
                    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")  # 格式化为 "年月日_时分秒"
                    name, ext = os.path.splitext(filename)  # 分离文件名和扩展名
                    new_filename = f"{city_name}_{name}_{timestamp}{ext}"  # 新文件名

                    # 构建新的文件路径
                    new_file_path = os.path.join(clear_file_path, new_filename)

                    # 保存处理后的CSV文件到新文件夹
                    df.to_csv(new_file_path, index=False, encoding='utf-8')
                    print(f"Processed and saved to: {new_file_path}")

                else:
                    error_msg = f"{file_path}: 不是 CSV 文件，跳过处理。"
                    error_log.append(error_msg)

        # 将错误信息保存到 error_log.txt 中
        if error_log:
            # 为错误日志文件名也添加时间戳
            error_log_filename = f"error_log_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt"
            error_log_path = os.path.join(clear_file_path, error_log_filename)
            with open(error_log_path, "w", encoding="utf-8") as log_file:
                for error in error_log:
                    log_file.write(error + "\n")
            print(f"部分文件处理失败，错误日志已保存到: {error_log_path}")
        else:
            print("所有文件处理完成，未发现错误。")

    @staticmethod
    def delete_data_by_city(keys, cities):
        """
        根据传入的 keys 和 cities，删除对应表中指定城市的数据
        :param keys: 需要删除数据的表名键（列表）
        :param cities: 需要删除的城市列表（列表）
        """
        # 初始化数据库配置
        config = ProvincialPlatformConfigDev()

        # 遍历 keys，删除对应表中的数据
        for key in keys:
            # 获取表名
            table_name = csv_to_table_mapping.get(key)
            if not table_name:
                print(f"未找到对应的表: {key}")
                continue

            # 构建删除条件
            cities_str = ", ".join([f"'{city}'" for city in cities])  # 将城市列表转换为 SQL 字符串
            condition = f"city IN ({cities_str})"

            # 调用删除方法
            try:
                JdbcConfigUtils.delete_table_data_v2(
                    _database_name=config.DATABASE_NAME,
                    _table_name=table_name,
                    _condition=condition,
                    _config=config
                )
                print(f"已删除表 {table_name} 中城市 {cities} 的数据")
            except Exception as e:
                print(f"删除表 {table_name} 数据时出错: {e}")


    @staticmethod
    def delete_data_by_city_districts(keys, city, districts=None):
        """
        根据传入的 keys、city 和可选的 districts，删除对应表中指定城市及区域的数据
        :param keys: 需要删除数据的表名键（列表）
        :param city: 需要删除的城市（字符串）
        :param districts: 可选，需要删除的区域列表（列表）
        """
        # 初始化数据库配置
        config = ProvincialPlatformConfigDev()

        # 遍历 keys，删除对应表中的数据
        for key in keys:
            # 获取表名
            table_name = csv_to_table_mapping.get(key)
            if not table_name:
                print(f"未找到对应的表: {key}")
                continue

            # 构建删除条件
            if districts:
                # 生成 district IN (...) 条件
                districts_str = ", ".join([f"'{d}'" for d in districts])
                condition = f"city = '{city}' AND district IN ({districts_str})"
                log_msg = f"城市 {city} 的区域 {districts}"
            else:
                # 仅根据 city 删除
                condition = f"city = '{city}'"
                log_msg = f"城市 {city}"

            # 调用删除方法
            try:
                JdbcConfigUtils.delete_table_data_v2(
                    _database_name=config.DATABASE_NAME,
                    _table_name=table_name,
                    _condition=condition,
                    _config=config
                )
                print(f"已删除表 {table_name} 中{log_msg} 的数据")
            except Exception as e:
                print(f"删除表 {table_name} 数据时出错: {e}")


    @staticmethod
    def batch_delete_from_csv_data_v2(df, file_name):
        """
        从 pandas DataFrame 中读取 city 字段，并根据该字段的内容批量删除表中的数据
        :param df: pandas DataFrame，包含 CSV 数据
        :param file_name: 需要删除的表
        """
        # 初始化数据库配置
        config = ProvincialPlatformConfigDev()
        # 开始删除表数据提示
        print(f"开始删除{file_name}表数据")

        # 检查 DataFrame 是否包含必要的列
        if 'city' not in df.columns:
            print("CSV 数据中缺少 city 字段")
            return

        # 清理包含 NaN 的行
        df_cleaned = df.dropna(subset=['city'])

        # 根据 CSV 文件名获取对应的表名
        table_name = base_info_csv_to_table_mapping.get(file_name)
        if not table_name:
            print(f"未找到 CSV 文件 {file_name} 对应的表名映射")
            return

        # 获取唯一的 city 列表并检查是否为空
        unique_cities = df_cleaned['city'].unique().tolist()
        if not unique_cities:
            print("没有有效的 city 值用于删除")
            return

        # 转义单引号并构建安全的 SQL 条件
        safe_cities = []
        for city in unique_cities:
            city_str = str(city)
            city_escaped = city_str.replace("'", "''")  # 转义单引号防止 SQL 注入
            safe_cities.append(f"'{city_escaped}'")

        # 组合成 SQL IN 条件
        delete_condition = f"city IN ({', '.join(safe_cities)})"

        # 使用事务执行批量删除
        try:
            JdbcConfigUtils.delete_table_data(
                _database_name=config.DATABASE_NAME,
                _table_name=table_name,
                _condition=delete_condition,
                _config=config
            )
        except Exception as e:
            print(f"批量删除过程中出错: {e}")
    @staticmethod
    def invert_area_dict(area_dict):
        """
        反转区域字典中的编码和区域名称，包括顶级键，并保持整体结构不变。
        :param area_dict: 包含区域及其编码的字典
        :return: 反转后的字典
        """
        inverted_result = {}

        for city_name, details in area_dict.items():
            # 获取城市的编码作为新的顶级键
            city_code = details['name']

            # 初始化新城市的条目
            inverted_city = {'name': city_name}  # 将城市名作为code的值

            # 初始化children反转后的结果
            inverted_children = {}

            # 遍历子区域并进行反转
            if 'children' in details:
                for child_name, child_code in details['children'].items():
                    inverted_children[child_code] = child_name

            inverted_city['children'] = inverted_children

            # 使用城市编码作为键存入到结果中
            inverted_result[city_code] = inverted_city

        return inverted_result
    @staticmethod
    # 定义一个函数来清理文本
    def clean_text(text):
        """
        保留文本中的中文字符、英文字符和数字，移除其他字符。
        """
        # 使用正则表达式保留中文字符（\u4e00-\u9fff）、英文字符（a-zA-Z）和数字（0-9）
        cleaned_text = re.sub(r'[^\w\u4e00-\u9fff]+', '', text)
        # \w 匹配字母数字及下划线，为了更精确地只保留英文和数字，可以替换为 [a-zA-Z0-9]
        # 因此上面的正则表达式也可以写成 r'[^\a-zA-Z0-9\u4e00-\u9fff]+'
        return cleaned_text



if __name__ == '__main__':
    pass
